package org.example.uhy.domain.model.coupon;

import org.example.uhy.domain.model.coupon.event.CouponActivated;
import org.example.uhy.domain.model.coupon.event.CouponCancelled;
import org.example.uhy.domain.model.coupon.event.CouponConsumed;
import org.example.uhy.domain.model.coupon.event.CouponExpired;
import org.example.uhy.exceptions.BusinessException;
import org.example.uhy.supports.mechanism.DomainEventPublisher;

import java.time.LocalDateTime;
import java.util.function.Consumer;

/**
 * 卡券简单状态机
 */
public class CouponStateMachine {
    private Coupon container;
    //    private CouponStatus currentStatus;
//    private CouponStatus prevStatus;
    // 通过调用{@link Consumer#accept}方法，可以更新卡券对象的内部状态
    Consumer<CouponStatus> watcher;

    public CouponStateMachine(Coupon coupon, Consumer<CouponStatus> watcher) {
        this.container = coupon;
        this.watcher = watcher;
    }

    /**
     * 尝试转移到{@code CouponStatus.Activated 可用状态}
     * <p>
     * 当前状态为{@code CouponStatus.Created 初始状态}，并且卡券在有效期期间，卡券转移到到{@code CouponStatus.Activated 可用状态}。
     * 如果成功转移到{@code CouponStatus.Activated 可用状态}，则发布{@link CouponActivated}领域事件
     */
    public void activate() {
        if (currentStatus() == CouponStatus.Created && container.effectiveTime().inRange(LocalDateTime.now())) {
            if (this.changeStatus(CouponStatus.Activated)) {
                DomainEventPublisher.instance().publish(new CouponActivated(container.couponId()));
            }
        }
    }

    /**
     * 消费卡券状态转移
     * <p>
     * 状态转移之前，先尝试可用状态转移{@link #activate()}和过期状态转移{@link #expire()}
     * 如果成功转移到{@code CouponStatus.Consumed 已消费状态}，则发布{@link CouponConsumed}领域事件
     */
    public void consume() {
        // 先可用状态尝试
        this.activate();
        // 在过期状态尝试
        this.expire();
        //
        if (this.changeStatus(CouponStatus.Consumed)) {
            DomainEventPublisher.instance().publish(new CouponConsumed(container.couponId()));
        }
    }

    /**
     * 尝试转移到{@code CouponStatus.Expired 过期状态}
     * <p>
     * 当前状态为{@code CouponStatus.Created 初始状态}或{@code CouponStatus.Activated 可用状态}，并且超出卡券失效时间，卡券转移到到{@code CouponStatus.Expired 过期状态}。
     * 如果成功转移到{@code CouponStatus.Expired 过期状态}，则发布{@link CouponExpired}领域事件
     */
    public void expire() {
        if ((currentStatus() == CouponStatus.Created || currentStatus() == CouponStatus.Activated) &&
                LocalDateTime.now().isAfter(container.effectiveTime().getToTime())) {
            if (this.changeStatus(CouponStatus.Expired)) {
                DomainEventPublisher.instance().publish(new CouponExpired(container.couponId()));
            }
        }
    }

    /**
     * 取消卡券消费状态转移
     * <p>
     * 当前状态为{@code CouponStatus.Consumed 已消费状态}，卡券转移到到{@code CouponStatus.Activated 可用状态}。
     * 同时发布{@link CouponActivated}领域事件
     */
    public void back() {
        if (currentStatus() == CouponStatus.Consumed) {
            if (this.setStatusDirectly(CouponStatus.Activated)) {
                DomainEventPublisher.instance().publish(new CouponActivated(container.couponId()));
            }
        }
    }

    /**
     * 废弃卡券状态转移
     * <p>
     * 如果成功转移到{@code CouponStatus.Cancelled 废弃状态}，则发布{@link CouponCancelled}领域事件
     */
    public void cancel() {
        if (this.changeStatus(CouponStatus.Cancelled)) {
            DomainEventPublisher.instance().publish(new CouponCancelled(container.couponId()));
        }
    }

    /**
     * 直接设置状态
     *
     * @param nextStatus
     * @return 状态是否变更
     */
    private boolean setStatusDirectly(CouponStatus nextStatus) {
        // this.currentStatus = currentStatus;
        if (nextStatus != currentStatus()) {
            this.watcher.accept(nextStatus);
            return true;
        } else {
            return false;
        }
    }

    /**
     * 状态变更
     *
     * @param nextStatus 下一个状态
     * @return 状态是否变更
     */
    private boolean changeStatus(CouponStatus nextStatus) {
        CouponStatus newState = currentStatus();
        switch (currentStatus()) {
            case Created:
                if (nextStatus == CouponStatus.Consumed) {
                    throw new BusinessException("卡券不能直接从初始状态到消费状态");
                } else {
                    newState = nextStatus;
                }
            case Activated:
                if (nextStatus == CouponStatus.Created) {
                    throw new BusinessException("卡券不能从可用状态回退到初始状态");
                } else {
                    newState = nextStatus;
                }
                break;
            case Consumed:
                if (nextStatus == CouponStatus.Activated) {
                    newState = nextStatus;
                } else if (nextStatus == CouponStatus.Consumed) {
                    throw new BusinessException("卡券不能重复核销");
                } else {
                    throw new BusinessException("卡券只能从核销状态回退到可用状态");
                }
            case Expired:
                throw new BusinessException("卡券过期状态为最终状态，不可以切换状态");
            case Cancelled:
                throw new BusinessException("卡券作废状态为最终状态，不可以切换状态");
        }
//        if (tempStatus != currentStatus()) {
//            prevStatus = tempStatus;
//        }
        if (newState != currentStatus()) {
            this.watcher.accept(newState);
            return true;
        } else {
            return false;
        }
    }

    public CouponStatus currentStatus() {
        return container.currentStatus();
    }

//    public CouponStatus prevStatus() {
//        return prevStatus;
//    }
}
